/**
 * 
 */
package edu.umd.clip.lm.factors;

import java.util.*;
import java.util.concurrent.locks.*;
import org.w3c.dom.*;

import edu.umd.clip.lm.util.*;
/**
 * @author Denis Filimonov <den@cs.umd.edu>
 *
 */

public final class Dictionary {

	protected final static String XML_ELEMENT_NAME = "dictionary";
	protected final static String XML_ITEM_NAME = "dict-item";
	protected final static String XML_ATTR_PARENT_VAL_NAME = "parent-val";
	
	private Vector<String> elements;
	private HashMap<String,Integer> elementsByWord;
	private ReentrantReadWriteLock rwLock;
	private boolean locked;
	
	private final static String NULL_WORD = "<NULL>";
	private final static int NULL_VALUE = 0;
	private final static String UNK_WORD = "<unk>";
	private final static int UNK_VALUE = 1;
	private final static String START_WORD = "<s>";
	private final static int START_VALUE = 2;
	private final static String END_WORD = "</s>";
	private final static int END_VALUE = 3;
	
	public final static int FIRST_NORMAL_VALUE = 4;
	
	public Dictionary(int size) {
		elements = new Vector<String>(size+FIRST_NORMAL_VALUE);
		elementsByWord = new HashMap<String,Integer>(size+FIRST_NORMAL_VALUE);
		rwLock = new ReentrantReadWriteLock();
		locked = false;
		
		//elements.setSize(size+1);
		elements.addElement(NULL_WORD);
		elementsByWord.put(NULL_WORD, new Integer(NULL_VALUE));
		elements.add(UNK_WORD);
		elementsByWord.put(UNK_WORD, new Integer(UNK_VALUE));
		elements.add(START_WORD);
		elementsByWord.put(START_WORD, new Integer(START_VALUE));
		elements.add(END_WORD);
		elementsByWord.put(END_WORD, new Integer(END_VALUE));
	}
	
	static protected Dictionary createDictionary(Element xmlnode) {
		NodeList itemList = XMLUtils.getChildrenElementsByTagName(xmlnode, Dictionary.XML_ITEM_NAME);
		//assert(itemList.getLength() > 0);
		Dictionary dict = new Dictionary(itemList.getLength());
		
		for(int i = 0; i< itemList.getLength(); ++i) {
			dict.add(itemList.item(i).getTextContent());
		}
		dict.lock();
		return dict;
	}
	
	protected Element createXML(Document doc) {
		return createXML(doc, true);
	}
	
	protected Element createXML(Document doc, boolean includeSpecial) {
		Element element = doc.createElement(XML_ELEMENT_NAME);
		rwLock.readLock().lock();
		int i = includeSpecial ? 0 : FIRST_NORMAL_VALUE;
		for(; i<elements.size(); ++i) {
			String item = elements.get(i);
			Element node = doc.createElement(XML_ITEM_NAME);
			node.setTextContent(item);
			element.appendChild(node);
		}
		rwLock.readLock().unlock();
		return element;
	}
	
	public String getWord(int elem) {
		String s = null;
		if (locked) {
			s = elements.get(elem);
		} else {
			rwLock.readLock().lock();
			try {
				s = elements.get(elem);
			} finally {
				rwLock.readLock().unlock();
			}
		}
		return s;
	}
	
	private int _getId(String name) {
		if (name == null) return NULL_VALUE;
		
		Integer val = elementsByWord.get(name);
		if (val == null) {
			return UNK_VALUE;
		}
		return val.intValue();
	}
	
	public int getId(String name) {
		if (locked) {
			return _getId(name);
		}
		rwLock.readLock().lock();
		int id;
		try {
			id = _getId(name);
		} finally {
			rwLock.readLock().unlock();
		}			
		return id;
	}
	
	public boolean exists(String name) {
		if (locked) {
			return elementsByWord.containsKey(name);
		}
		rwLock.readLock().lock();
		boolean res = elementsByWord.containsKey(name);
		rwLock.readLock().unlock();
		return res;
	}
	
	public int add(String name) {
		assert(locked == false);
		rwLock.writeLock().lock();
		Integer val = elementsByWord.get(name);
		if (val == null) {
			val = new Integer(elements.size());
			elements.addElement(name);
			elementsByWord.put(name, val);
		}
		rwLock.writeLock().unlock();
		return val.intValue();
	}
	
	public static boolean isNull(int val) {
		return val == NULL_VALUE;
	}
	
	public static boolean isNullWord(String word) {
		return NULL_WORD.equals(word);
	}
	
	public static boolean isUnk(int val) {
		return val == UNK_VALUE;
	}
	
	public static boolean isUnkWord(String word) {
		return UNK_WORD.equals(word);
	}
	
	public static boolean isStart(int val) {
		return val == START_VALUE;
	}
	
	public static boolean isStartWord(String word) {
		return START_WORD.equals(word);
	}
	
	public static boolean isEnd(int val) {
		return val == END_VALUE;
	}
	
	public static boolean isEndWord(String word) {
		return END_WORD.equals(word);
	}
	
	public static int getUnk() {
		return UNK_VALUE;
	}
	
	public static String getUnkWord() {
		return UNK_WORD;
	}
	
	public static int getNull() {
		return NULL_VALUE;
	}
	
	public static String getNullWord() {
		return NULL_WORD;
	}
	
	public static int getStart() {
		return START_VALUE;
	}
	
	public static String getStartWord() {
		return START_WORD;
	}
	
	public static int getEnd() {
		return END_VALUE;
	}
	
	public static String getEndWord() {
		return END_WORD;
	}
	
	public int size() {
		return elements.size();
	}
	
	public void lock() {
		locked = true;
	}
	
	public boolean isOpen() {
		return !locked;
	}
	
	public DictionaryIterator iterator() {
		return new DictionaryIterator(this, false);
	}
	
	public DictionaryIterator iterator(boolean countSpecial) {
		return new DictionaryIterator(this, countSpecial);
	}
}
